﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;

namespace CAION2.Helpers
{
    public class GridViewSort
    {
        #region Public attached properties

        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    null,
                    (o, e) =>
                    {
                        ItemsControl listView = o as ItemsControl;
                        if (listView != null)
                        {
                            if (!GetAutoSort(listView)) // Don't change click handler if AutoSort enabled
                            {
                                if (e.OldValue != null && e.NewValue == null)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (e.OldValue == null && e.NewValue != null)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );

        public static bool GetAutoSort(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSortProperty);
        }

        public static void SetAutoSort(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSortProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSort.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSortProperty =
            DependencyProperty.RegisterAttached(
                "AutoSort",
                typeof(bool),
                typeof(GridViewSort),
                new UIPropertyMetadata(
                    false,
                    (o, e) =>
                    {
                        ListView listView = o as ListView;
                        if (listView != null)
                        {
                            if (GetCommand(listView) == null) // Don't change click handler if a command is set
                            {
                                bool oldValue = (bool) e.OldValue;
                                bool newValue = (bool) e.NewValue;
                                if (oldValue && !newValue)
                                {
                                    listView.RemoveHandler(GridViewColumnHeader.ClickEvent,
                                                           new RoutedEventHandler(ColumnHeader_Click));
                                }
                                if (!oldValue && newValue)
                                {
                                    listView.AddHandler(GridViewColumnHeader.ClickEvent,
                                                        new RoutedEventHandler(ColumnHeader_Click));
                                }
                            }
                        }
                    }
                )
            );
        
        public static string GetPropertyName(DependencyObject obj)
        {
            return (string)obj.GetValue(PropertyNameProperty);
        }

        public static void SetPropertyName(DependencyObject obj, string value)
        {
            obj.SetValue(PropertyNameProperty, value);
        }

        // Using a DependencyProperty as the backing store for PropertyName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.RegisterAttached(
                "PropertyName",
                typeof(string),
                typeof(GridViewSort),
                new UIPropertyMetadata(null)
            );

        public static string GetPropertyExtendedName(DependencyObject obj)
        {
            return (string)obj.GetValue(PropertyExtendedNameProperty);
        }

        public static void SetPropertyExtendedName(DependencyObject obj, string value)
        {
            obj.SetValue(PropertyExtendedNameProperty, value);
        }

        public static readonly DependencyProperty PropertyExtendedNameProperty =
            DependencyProperty.RegisterAttached(
                "PropertyExtendedName",
                typeof(string),
                typeof(GridViewSort),
                new UIPropertyMetadata(null)
            );

        public static bool GetGlyphIsLeft(DependencyObject obj)
        {
            return (bool)obj.GetValue(GlyphIsLeftProperty);
        }

        public static void SetGlyphIsLeft(DependencyObject obj, bool value)
        {
            obj.SetValue(PropertyExtendedNameProperty, value);
        }

        public static readonly DependencyProperty GlyphIsLeftProperty =
            DependencyProperty.RegisterAttached(
                "GlyphIsLeft",
                typeof(bool),
                typeof(GridViewSort),
                new UIPropertyMetadata(false)
            );

        public static bool GetShowSortGlyph(DependencyObject obj)
        {
            return (bool)obj.GetValue(ShowSortGlyphProperty);
        }

        public static void SetShowSortGlyph(DependencyObject obj, bool value)
        {
            obj.SetValue(ShowSortGlyphProperty, value);
        }

        // Using a DependencyProperty as the backing store for ShowSortGlyph.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowSortGlyphProperty =
            DependencyProperty.RegisterAttached("ShowSortGlyph", typeof(bool), typeof(GridViewSort), new UIPropertyMetadata(true));

        public static ImageSource GetSortGlyphAscending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphAscendingProperty);
        }

        public static void SetSortGlyphAscending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphAscendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphAscending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphAscendingProperty =
            DependencyProperty.RegisterAttached("SortGlyphAscending", typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        public static ImageSource GetSortGlyphDescending(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(SortGlyphDescendingProperty);
        }

        public static void SetSortGlyphDescending(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(SortGlyphDescendingProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortGlyphDescending.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SortGlyphDescendingProperty =
            DependencyProperty.RegisterAttached("SortGlyphDescending", typeof(ImageSource), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Private attached properties

        private static GridViewColumnHeader GetSortedColumnHeader(DependencyObject obj)
        {
            return (GridViewColumnHeader)obj.GetValue(SortedColumnHeaderProperty);
        }

        private static void SetSortedColumnHeader(DependencyObject obj, GridViewColumnHeader value)
        {
            obj.SetValue(SortedColumnHeaderProperty, value);
        }

        // Using a DependencyProperty as the backing store for SortedColumn.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty SortedColumnHeaderProperty =
            DependencyProperty.RegisterAttached("SortedColumnHeader", typeof(GridViewColumnHeader), typeof(GridViewSort), new UIPropertyMetadata(null));

        #endregion

        #region Column header click event handler

        private static void ColumnHeader_Click(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked != null && headerClicked.Column != null)
            {
                string propertyName = GetPropertyName(headerClicked.Column);
                if (!string.IsNullOrEmpty(propertyName))
                {
                    ListView listView = GetAncestor<ListView>(headerClicked);
                    if (listView != null)
                    {
                        ICommand command = GetCommand(listView);
                        if (command != null)
                        {
                            if (command.CanExecute(propertyName))
                            {
                                command.Execute(propertyName);
                            }
                        }
                        else if (GetAutoSort(listView))
                        {
                            string propertyExtendedNames = GetPropertyExtendedName(headerClicked.Column);
                            ApplySort(listView.Items, propertyName, listView, headerClicked, 
                                      propertyExtendedNames, GetGlyphIsLeft(headerClicked.Column));
                        }
                    }
                }
            }
        }

        #endregion

        #region Helper methods

        public static T GetAncestor<T>(DependencyObject reference) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(reference);
            while (!(parent is T))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            if (parent != null)
                return (T)parent;
            else
                return null;
        }

        public static void ApplySort(ICollectionView view, string propertyName, ListView listView, GridViewColumnHeader sortedColumnHeader,
                                     string propertyExtendedNames, bool glyphIsLeft)
        {
            ListSortDirection direction = ListSortDirection.Ascending;
            if (view.SortDescriptions.Count > 0)
            {
                SortDescription currentSort = view.SortDescriptions[0];
                if (currentSort.PropertyName == propertyName)
                {
                    if (currentSort.Direction == ListSortDirection.Ascending)
                        direction = ListSortDirection.Descending;
                    else
                        direction = ListSortDirection.Ascending;
                }
                view.SortDescriptions.Clear();

                GridViewColumnHeader currentSortedColumnHeader = GetSortedColumnHeader(listView);
                if (currentSortedColumnHeader != null)
                {
                    RemoveSortGlyph(currentSortedColumnHeader);
                }
            }
            if (!string.IsNullOrEmpty(propertyName))
            {
                view.SortDescriptions.Add(new SortDescription(propertyName, direction));

                if (!string.IsNullOrEmpty(propertyExtendedNames))
                {
                    string[] extPropertyNames = propertyExtendedNames.Split(',');
                    foreach (string s in extPropertyNames)
                    {
                        view.SortDescriptions.Add(new SortDescription(s, direction));
                    }
                }

                if (GetShowSortGlyph(listView))
                    AddSortGlyph(
                        sortedColumnHeader,
                        direction,
                        direction == ListSortDirection.Ascending ? GetSortGlyphAscending(listView) : GetSortGlyphDescending(listView),
                        glyphIsLeft);
                SetSortedColumnHeader(listView, sortedColumnHeader);
            }
        }

        private static void AddSortGlyph(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph, bool glyphIsLeft)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            adornerLayer.Add(
                new SortGlyphAdorner(
                    columnHeader,
                    direction,
                    sortGlyph,
                    glyphIsLeft
                    ));
        }

        private static void RemoveSortGlyph(GridViewColumnHeader columnHeader)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(columnHeader);
            Adorner[] adorners = adornerLayer.GetAdorners(columnHeader);
            if (adorners != null)
            {
                foreach (Adorner adorner in adorners)
                {
                    if (adorner is SortGlyphAdorner)
                        adornerLayer.Remove(adorner);
                }
            }
        }

        #endregion

        #region SortGlyphAdorner nested class

        private class SortGlyphAdorner : Adorner
        {
            private GridViewColumnHeader _columnHeader;
            private ListSortDirection _direction;
            private ImageSource _sortGlyph;
            private bool _glyphIsLeft;

            public SortGlyphAdorner(GridViewColumnHeader columnHeader, ListSortDirection direction, ImageSource sortGlyph, bool glyphIsLeft)
                : base(columnHeader)
            {
                _columnHeader = columnHeader;
                _direction = direction;
                _sortGlyph = sortGlyph;
                _glyphIsLeft = glyphIsLeft;
            }

            private Geometry GetDefaultGlyph()
            {
                PathGeometry pathGeometry = new PathGeometry();
                Path newPath = (Path)XamlReader.Parse("<Path xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'  Width='10' Height='10' Stretch='Fill' Fill='#FF000000' Data='M5.3904839,6.138012C8.3707542,6.138012 10.780972,8.553011 10.780972,11.528009 10.780972,14.506007 8.3707542,16.919005 5.3904839,16.919005 2.4102173,16.919005 0,14.506007 0,11.528009 0,8.553011 2.4102173,6.138012 5.3904839,6.138012z M25.555002,3.4420112C30.012231,3.4420109 33.639976,7.0630084 33.639976,11.527005 33.639976,15.993001 30.012231,19.613999 25.555002,19.613999 21.08778,19.613999 17.47003,15.993001 17.47003,11.527005 17.47003,7.0630084 21.08778,3.4420109 25.555002,3.4420112z M51.143494,0C57.515409,4.6731384E-07 62.666988,5.1582242 62.666988,11.527497 62.666988,17.89578 57.515409,23.06 51.143494,23.06 44.771577,23.06 39.609987,17.89578 39.609987,11.527497 39.609987,5.1582242 44.771577,4.6731384E-07 51.143494,0z' HorizontalAlignment='Right' VerticalAlignment='Center' Margin='0'/>");
                if (_direction == ListSortDirection.Descending)
                {
                    newPath = (Path)XamlReader.Parse("<Path xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'  Width='10' Height='10' Stretch='Fill' Fill='#FF000000' Data='M57.275462,6.1399681C60.242972,6.1399686 62.660948,8.5527484 62.660948,11.530477 62.660948,14.503207 60.242972,16.920987 57.275462,16.920987 54.297957,16.920987 51.879984,14.503207 51.879984,11.530477 51.879984,8.5527484 54.297957,6.1399686 57.275462,6.1399681z M37.100447,3.4449763C41.570711,3.4449761 45.190928,7.0649769 45.190928,11.529978 45.190928,15.993978 41.570711,19.616978 37.100447,19.616978 32.640185,19.616978 29.01997,15.993978 29.01997,11.529978 29.01997,7.0649769 32.640185,3.4449761 37.100447,3.4449763z M11.5285,0C17.89765,1.5966907E-07 23.057,5.1640022 23.057,11.53 23.057,17.901004 17.89765,23.06 11.5285,23.06 5.1593498,23.06 0,17.901004 0,11.53 0,5.1640022 5.1593498,1.5966907E-07 11.5285,0z' HorizontalAlignment='Left' VerticalAlignment='Top' Margin='0'/>");
                }
                pathGeometry.AddGeometry(newPath.Data);
                pathGeometry.Transform = new ScaleTransform(0.18, 0.20);

                return pathGeometry;
            }

            protected override void OnRender(DrawingContext drawingContext)
            {
                base.OnRender(drawingContext);

                if (_sortGlyph != null)
                {
                    double x = 4; // _columnHeader.ActualWidth - 18;
                    double y = _columnHeader.ActualHeight / 2 - 7;
                    Rect rect = new Rect(x, y, 10, 6);
                    drawingContext.DrawImage(_sortGlyph, rect);
                }
                else
                {
                    double x;
                    if (_glyphIsLeft)
                    {
                        x = 4;
                    }
                    else
                    {
                        x = _columnHeader.ActualWidth - 16;
                    }
                    
                    double y = _columnHeader.ActualHeight / 2 - 3;
                    Rect rect = new Rect(x, y, 10, 6);

                    ImageSource imSo = new DrawingImage(new GeometryDrawing(_columnHeader.Foreground, null, GetDefaultGlyph()));
                    drawingContext.DrawImage(imSo, rect);
                    //drawingContext.DrawGeometry(Brushes.LightGray, new Pen(Brushes.Gray, 1.0), GetDefaultGlyph());
                }
            }
        }

        #endregion
        
        
    }
}
